// Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;
using Microsoft.WindowsAPICodePack.DirectX.Direct3D11;
using Microsoft.WindowsAPICodePack.DirectX.DXGI;

namespace Microsoft.WindowsAPICodePack.Samples.Direct3D11
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        private struct D3DVector3F
        {
            public float x;
            public float y;
            public float z;
        }

        private struct SimpleVertex
        {
            public D3DVector3F Pos;
        };

        public Window1()
        {
            InitializeComponent();
        }

        #region Private Fields
        
        D3DDevice device;
        DeviceContext deviceContext;
        RenderTargetView renderTargetView;
        PixelShader pixelShader;
        VertexShader vertexShader;
        D3DBuffer vertexBuffer;
        SwapChain swapChain;
        
        #endregion

        #region Window_Loaded()
        /// <summary>
        /// Handles the Loaded event of the window.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            InitDevice();
            host.Render = RenderScene;
        }
        #endregion

        #region Init device

        /// <summary>
        /// Init device and required resources
        /// </summary>
        private void InitDevice()
        {
            // device creation
            device = D3DDevice.CreateDeviceAndSwapChain(host.Handle, out swapChain);
            deviceContext = device.GetImmediateContext();


            Texture2D texture2D = swapChain.GetBuffer<Texture2D>(0);
            renderTargetView = device.CreateRenderTargetView(texture2D, null);
            deviceContext.OM.SetRenderTargets(new RenderTargetView[] { renderTargetView }, null);
            texture2D.Dispose();

            // viewport
            SwapChainDescription desc = swapChain.GetDescription();
            Viewport viewport = new Viewport();
            viewport.Width = desc.BufferDesc.Width;
            viewport.Height = desc.BufferDesc.Height;
            viewport.MinDepth = 0.0f;
            viewport.MaxDepth = 1.0f;
            viewport.TopLeftX = 0;
            viewport.TopLeftY = 0;

            deviceContext.RS.SetViewports(new Viewport[] { viewport });


            // raster state


            // vertex shader & layout            

            // Open precompiled vertex shader
            // This file was compiled using: fxc Render.hlsl /T vs_4_0 /EVertShader /FoRender.vs
            using (Stream stream = Application.ResourceAssembly.GetManifestResourceStream("Tutorial02_WPF.Render.vs"))
            {
                vertexShader = device.CreateVertexShader(stream);
            }

            deviceContext.VS.SetShader(vertexShader, null);

            // input layout is for the vert shader
            InputElementDescription inputElementDescription = new InputElementDescription();
            inputElementDescription.SemanticName = "POSITION";
            inputElementDescription.SemanticIndex = 0;
            inputElementDescription.Format = Format.R32G32B32_FLOAT;
            inputElementDescription.InputSlot = 0;
            inputElementDescription.AlignedByteOffset = 0;
            inputElementDescription.InputSlotClass = InputClassification.PerVertexData;
            inputElementDescription.InstanceDataStepRate = 0;

            InputLayout inputLayout;
            using (Stream stream = Application.ResourceAssembly.GetManifestResourceStream("Tutorial02_WPF.Render.vs"))
            {
                inputLayout = device.CreateInputLayout(new InputElementDescription[] { inputElementDescription }, stream);
            }
            deviceContext.IA.SetInputLayout(inputLayout);

            // Open precompiled vertex shader
            // This file was compiled using: fxc Render.hlsl /T ps_4_0 /EPixShader /FoRender.ps
            using (Stream stream = Application.ResourceAssembly.GetManifestResourceStream("Tutorial02_WPF.Render.ps"))
            {
                pixelShader = device.CreatePixelShader(stream);
            }
            deviceContext.PS.SetShader(pixelShader, null);


            // create some geometry to draw (1 triangle)

            SimpleVertex[] verts2 = new SimpleVertex[3];

            verts2[0].Pos.x = 0.0f;
            verts2[0].Pos.y = 0.5f;
            verts2[0].Pos.z = 0.5f;

            verts2[1].Pos.x = 0.5f;
            verts2[1].Pos.y = -0.5f;
            verts2[1].Pos.z = 0.5f;

            verts2[2].Pos.x = -0.5f;
            verts2[2].Pos.y = -0.5f;
            verts2[2].Pos.z = 0.5f;


            // put the vertices into a vertex buffer

            BufferDescription bufferDescription = new BufferDescription();
            bufferDescription.Usage = Usage.Default;
            bufferDescription.ByteWidth = (uint)(Marshal.SizeOf(typeof(SimpleVertex)) * verts2.Length);
            bufferDescription.BindFlags = BindFlag.VertexBuffer;

            SubresourceData subresourceData = new SubresourceData();

            unsafe
            {
                fixed (SimpleVertex* pVerts = verts2)
                {
                    subresourceData.SysMem = new IntPtr(pVerts);
                    vertexBuffer = device.CreateBuffer(bufferDescription, subresourceData);
                }
            }

            deviceContext.IA.SetVertexBuffers(0, new D3DBuffer[] { vertexBuffer }, new uint[] { 12 }, new uint[] { 0 });
            deviceContext.IA.SetPrimitiveTopology(PrimitiveTopology.TriangleList);
        }

        #endregion

        #region Render Scene

        /// <summary>
        /// Draw scene
        /// </summary>
        private void RenderScene()
        {
            deviceContext.ClearRenderTargetView(renderTargetView, new ColorRGBA(new float[] { 0.2f, 0.125f, 0.3f, 1.0f }));

            deviceContext.Draw(3, 0);

            swapChain.Present(0, 0);
        }
        #endregion

    }
}
